(* -*- sml -*- *)
(**
 * @copyright (C) 2021 SML# Development Team.
 * @author UENO Katsuhiro
 *)
structure MachineCode =
struct

  fun ifcons (x, y) (_::_) = x
    | ifcons (x, y) nil = y
  fun ifsingle (x, y) [_] = x
    | ifsingle (x, y) _ = y
  fun iftrue (x, y) true = x
    | iftrue (x, y) false = y
  fun ifsome (x, y) (SOME _) = x
    | ifsome (x, y) NONE = y

  type loc = Loc.loc

  (*%
   * @formatter(RuntimeCalc.ty) RuntimeCalc.format_ty
   *)
  (*% @prefix formatWithType_
   * @formatter(RuntimeCalc.ty) RuntimeCalc.formatWithType_ty
   *)
  type ty =
      (*% @format(ty) ty *)
      (*% @prefix formatWithType_ @format(ty) ty *)
      RuntimeCalc.ty

  (*%
   * @formatter(RuntimeCalc.varInfo) RuntimeCalc.format_varInfo
   *)
  (*% @prefix formatWithType_
   * @formatter(RuntimeCalc.varInfo) RuntimeCalc.formatWithType_varInfo
   *)
  type varInfo =
      (*% @format(x) x *)
      (*% @prefix formatWithType_ @format(x) x *)
      RuntimeCalc.varInfo

  (*%
   * @formatter(BuiltinPrimitive.primitiveMachineCode)
   * BuiltinPrimitive.format_primitiveMachineCode
   *)
  (*%
   * @prefix formatWithType_
   * @formatter(BuiltinPrimitive.primitiveMachineCode)
   * BuiltinPrimitive.format_primitiveMachineCode
   * @formatter(Types.btvEnv) Types.format_btvEnv
   * @formatter(Types.ty) Types.format_ty
   * @formatter(ifsingle) ifsingle
   *)
  type primInfo =
      (*%
       * @format({primitive, ty})
       * primitive
       *)
      (*%
       * @prefix formatWithType_
       * @format({primitive, ty})
       * L2{ primitive +1 ":" +d ty }
       * @format:ty({boundtvars, argTyList: argTy argTys, resultTy})
       * "[" !N0{ 2[
       *   {boundtvars "."}
       *   +1
       *   argTys:ifsingle()(,"{")
       *   !N0{ argTys(argTy)("," +1)
       *        argTys:ifsingle()(,"}") }
       *   +1 "->" +d
       *   resultTy
       * ] "]" }
       *)
      {
        primitive : BuiltinPrimitive.primitiveMachineCode,
        ty : {boundtvars : Types.btvEnv,
              argTyList : Types.ty list,
              resultTy : Types.ty}
      }

  (*%
   * @formatter(RuntimeCalc.ncconst) RuntimeCalc.format_ncconst
   *)
  (*% @prefix formatWithType_
   * @formatter(RuntimeCalc.ncconst) RuntimeCalc.formatWithType_ncconst
   *)
  datatype mcconst = datatype RuntimeCalc.ncconst

  (*%
   * @formatter(ANormal.anvalue) ANormal.format_anvalue
   *)
  (*% @prefix formatWithType_
   * @formatter(ANormal.anvalue) ANormal.formatWithType_anvalue
   *)
  datatype mcvalue = datatype ANormal.anvalue

  (*% *)
  datatype objtype =
      (*% @format(v) "VECTOR(" v ")" *)
      OBJTYPE_VECTOR of mcvalue
    | (*% @format(v) "ARRAY(" v ")" *)
      OBJTYPE_ARRAY of mcvalue
    | (*% @format "OBJTYPE_UNBOXED_VECTOR" *)
      OBJTYPE_UNBOXED_VECTOR
    | (*% @format "RECORD" *)
      OBJTYPE_RECORD
    | (*% @format "INTINF" *)
      OBJTYPE_INTINF

  (*%
   * @formatter(SlotID.id) SlotID.format_id
   *)
  (*% @prefix formatWithType_
   * @formatter(SlotID.id) SlotID.format_id
   *)
  datatype address =
      (*%
       * @format(ptrExp)
       * "[" +d "ptr" +d ptrExp "]"
       *)
      (*% @prefix formatWithType_
       * @format(ptrExp)
       * "[" +d "ptr" +d ptrExp "]"
       *)
      MAPTR of mcvalue
    | (*%
       * @format(base)
       * "[" base "]"
       *)
      (*% @prefix formatWithType_
       * @format(base)
       * "[" base "]"
       *)
      MAPACKED of mcvalue
    | (*%
       * @format({base, offset})
       * "[" !N0{ offset +1 "+" +d base "]" }
       *)
      (*% @prefix formatWithType_
       * @format({base, offset})
       * "[" !N0{ offset +1 "+" +d base "]" }
       *)
      MAOFFSET of {base: mcvalue, offset: mcvalue}
    | (*%
       * @format({recordExp, fieldIndex})
       * "[" !N0{
       *   L2{ "#" +d fieldIndex }
       *   +1 "of"
       *   +1 recordExp
       * } "]"
       *)
      (*% @prefix formatWithType_
       * @format({recordExp, fieldIndex})
       * "[" !N0{
       *   L2{ "#" +d fieldIndex }
       *   +1 "of"
       *   +1 recordExp
       * } "]"
       *)
      MARECORDFIELD of
      {
        recordExp : mcvalue,
        fieldIndex : mcvalue
      }
    | (*%
       * @format({arrayExp, elemSize, elemIndex})
       * "[" !N0{
       *   elemIndex
       *   +1 "*s" +d elemSize
       *   +1 "of"
       *   +1 arrayExp
       * } "]"
       *)
      (*% @prefix formatWithType_
       * @format({arrayExp, elemSize, elemIndex})
       * "[" !N0{
       *   elemIndex
       *   +1 "*s" +d elemSize
       *   +1 "of"
       *   +1 arrayExp
       * } "]"
       *)
      MAARRAYELEM of
      {
        arrayExp : mcvalue,
        elemSize : mcvalue,
        elemIndex : mcvalue
      }

  (*%
   * @formatter(ExtraDataLabel.id) ExtraDataLabel.format_id
   * @formatter(ExternSymbol.id) ExternSymbol.format_id
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(SlotID.id) SlotID.format_id
   * @formatter(ifcons) ifcons
   * @formatter(ifsome) ifsome
   * @formatter(iftrue) iftrue
   *)
  (*% @prefix formatWithType_
   * @formatter(ExtraDataLabel.id) ExtraDataLabel.format_id
   * @formatter(ExternSymbol.id) ExternSymbol.format_id
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(SlotID.id) SlotID.format_id
   * @formatter(objtype) format_objtype
   * @formatter(ifcons) ifcons
   * @formatter(ifsome) ifsome
   * @formatter(iftrue) iftrue
   *)
  datatype mcexp_mid =
      (*%
       * @format({resultVar, dataLabel, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "intinf"
       *   +1 dataLabel
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, dataLabel, loc})
       * L8{ resultVar +d "=" 1[
       *   +1 "intinf"
       *   +1 dataLabel
       *   ";"
       * ] }
       *)
      MCINTINF of
      {
        resultVar : varInfo,       (* : (intInfTy, BOXEDty) *)
        dataLabel : ExtraDataLabel.id,
        loc : loc
      }
    | (*%
       * @format({resultVar: var varOpt, funExp, attributes,
       *          argExpList: arg args, handler: handler handlerOpt, loc})
       * L8{ 1[
       *   varOpt:ifsome()(varOpt(var) +d "=" +1,)
       *   "foreignapply"
       *   +1 funExp
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   handlerOpt:ifsome()(+1 "handle" +d handlerOpt(handler),)
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar: var varOpt, funExp, attributes,
       *          argExpList: arg args, handler: handler handlerOpt, loc})
       * L8{ 1[
       *   varOpt:ifsome()(varOpt(var) +d "=" +1,)
       *   "foreignapply"
       *   +1 funExp
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   handlerOpt:ifsome()(+1 "handle" +d handlerOpt(handler),)
       *   ";"
       * ] }
       *)
      MCFOREIGNAPPLY of
      {
        resultVar : varInfo option,
        funExp : mcvalue,
        argExpList : mcvalue list,
        attributes : FFIAttributes.attributes,
        handler : HandlerLabel.id option,
        loc : loc
      }
    | (*%
       * @format({resultVar, codeExp, closureEnvExp, instTyvars, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "exportcallback"
       *   +1 codeExp
       *   +1 closureEnvExp
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, codeExp, closureEnvExp, instTyvars, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "exportcallback"
       *   +1 codeExp
       *   +1 closureEnvExp
       *   ";"
       * ] }
       *)
      MCEXPORTCALLBACK of
      {
        resultVar : varInfo,
        codeExp : mcvalue,
        closureEnvExp : mcvalue,
        instTyvars : Types.btvEnv,
        loc : loc
      }
    | (*%
       * @format({resultVar, id, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "exvar"
       *   +1 "@ext:" id
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, id, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "exvar"
       *   +1 "@ext:" id
       *   ";"
       * ] }
       *)
      MCEXVAR of
      {
        resultVar : varInfo,       (* : ty *)
        id : ExternSymbol.id,
        loc : loc
      }
    | (*%
       * @format({dstAddr, srcAddr, copySize, loc})
       * L8{ 1[
       *   "memcpy_field"
       *   +1 dstAddr
       *   +1 srcAddr
       *   +1 copySize
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({dstAddr, srcAddr, copySize, loc})
       * L8{ 1[
       *   "memcpy_field"
       *   +1 dstAddr
       *   +1 srcAddr
       *   +1 copySize
       *   ";"
       * ] }
       *)
      MCMEMCPY_FIELD of
      {
        dstAddr : address,         (* : valueTy addr *)
        srcAddr : address,         (* : valueTy addr *)
        copySize : mcvalue,        (* : SIZEOF(valueTy) *)
        loc : loc
      }
    | (*%
       * @format({dstAddr, srcAddr, numElems, elemSize, loc})
       * L8{ 1[
       *   "memmove_unboxed_array"
       *   +1 dstAddr
       *   +1 srcAddr
       *   +1 numElems
       *   +1 elemSize
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({dstAddr, srcAddr, numElems, elemSize, loc})
       * L8{ 1[
       *   "memmove_unboxed_array"
       *   +1 dstAddr
       *   +1 srcAddr
       *   +1 numElems
       *   +1 elemSize
       *   ";"
       * ] }
       *)
      MCMEMMOVE_UNBOXED_ARRAY of
      {
        dstAddr : address,         (* : valueTy addr *)
        srcAddr : address,         (* : valueTy addr *)
        numElems : mcvalue,
        elemSize : mcvalue,        (* : SIZEOF(elemTy) *)
        loc : loc
      }
    | (*%
       * @format({dstArray, dstIndex, srcArray, srcIndex, numElems, loc})
       * L8{ 1[
       *   "memmove_boxed_array"
       *   +1 dstArray
       *   +1 dstIndex
       *   +1 srcArray
       *   +1 srcIndex
       *   +1 numElems
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({dstArray, dstIndex, srcArray, srcIndex, numElems, loc})
       * L8{ 1[
       *   "memmove_boxed_array"
       *   +1 dstArray
       *   +1 dstIndex
       *   +1 srcArray
       *   +1 srcIndex
       *   +1 numElems
       *   ";"
       * ] }
       *)
      MCMEMMOVE_BOXED_ARRAY of
      {
        srcArray : mcvalue,       (* elemTy array *)
        dstArray : mcvalue,       (* elemTy array *)
        srcIndex : mcvalue,
        dstIndex : mcvalue,
        numElems : mcvalue,
        loc : loc
      }
    | (*%
       * @format({resultVar, objType, payloadSize, allocSize, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "alloc"
       *   +1 "(" !N0{ objType "," +1 payloadSize ")" }
       *   +1 allocSize
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, objType, payloadSize, allocSize, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "alloc"
       *   +1 "(" !N0{ objType "," +1 payloadSize ")" }
       *   +1 allocSize
       *   ";"
       * ] }
       *)
      (* GC may not occur between MCALLOC and MCALLOC_COMPLETED *)
      MCALLOC of
      {
        resultVar : varInfo,       (* : mem *)
        objType : objtype,
        payloadSize : mcvalue,
        allocSize : mcvalue,       (* : SIZEOF(mem) *)
        loc : loc
      }
    | (*%
       * @format
       * "alloc completed;"
       *)
      (*% @prefix formatWithType_
       * @format
       * "alloc completed;"
       *)
      MCALLOC_COMPLETED
    | (*%
       * @format({handler: handler handlerOpt})
       * "check" handlerOpt:ifsome()(+1 "handle" +d handlerOpt(handler),) ";"
       *)
      (*% @prefix formatWithType_
       * @format({handler: handler handlerOpt})
       * "check" handlerOpt:ifsome()(+1 "handle" +d handlerOpt(handler),) ";"
       *)
      MCCHECK of
      {
        handler : HandlerLabel.id option
      }
    | (*%
       * @format({resultVar, copySizeVar, recordExp, loc})
       * L8{ 1[
       *   resultVar "," +d copySizeVar +d "="
       *   +1 "recorddup_alloc"
       *   +1 recordExp
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, copySizeVar, recordExp, loc})
       * L8{ 1[
       *   resultVar "," +d copySizeVar +d "="
       *   +1 "recorddup_alloc"
       *   +1 recordExp
       *   ";"
       * ] }
       *)
      (* GC may not occur between MCRECORDDUP_ALLOC and MCALLOC_COMPLETED *)
      MCRECORDDUP_ALLOC of
      {
        resultVar : varInfo,       (* : mem *)
        copySizeVar : varInfo,
        recordExp : mcvalue,
        loc : loc
      }
    | (*%
       * @format({dstRecord, srcRecord, copySize, loc})
       * L8{ 1[
       *   "recorddup_copy"
       *   +1 dstRecord
       *   +1 srcRecord
       *   +1 copySize
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({dstRecord, srcRecord, copySize, loc})
       * L8{ 1[
       *   "recorddup_copy"
       *   +1 dstRecord
       *   +1 srcRecord
       *   +1 copySize
       *   ";"
       * ] }
       *)
      MCRECORDDUP_COPY of
      {
        dstRecord : mcvalue,
        srcRecord : mcvalue,
        copySize : mcvalue,
        loc : loc
      }
    | (*%
       * @format({recordExp, recordSize, loc})
       * L8{ 1[
       *   "bzero"
       *   +1 recordExp
       *   +1 recordSize
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({recordExp, recordSize, loc})
       * L8{ 1[
       *   "bzero"
       *   +1 recordExp
       *   +1 recordSize
       *   ";"
       * ] }
       *)
      MCBZERO of
      {
        recordExp : mcvalue,
        recordSize : mcvalue,
        loc : loc
      }
    | (*%
       * @format({slotId, value, loc})
       * L8{ 1[
       *   "saveslot"
       *   +1 slotId
       *   +1 value
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({slotId, value, loc})
       * L8{ 1[
       *   "saveslot"
       *   +1 slotId
       *   +1 value
       *   ";"
       * ] }
       *)
      MCSAVESLOT of
      {
        slotId : SlotID.id,        (* : tau slot *)
        value : mcvalue,           (* : tau *)
        loc : loc
      }
    | (*%
       * @format({resultVar, slotId, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "loadslot"
       *   +1 slotId
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, slotId, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "loadslot"
       *   +1 slotId
       *   ";"
       * ] }
       *)
      MCLOADSLOT of
      {
        resultVar : varInfo,
        slotId : SlotID.id,
        loc : loc
      }
    | (*%
       * @format({resultVar, srcAddr, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "load"
       *   +1 srcAddr
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, srcAddr, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "load"
       *   +1 srcAddr
       *   ";"
       * ] }
       *)
      MCLOAD of
      {
        resultVar : varInfo,
        srcAddr : address,
        loc : loc
      }
    | (*%
       * @format({resultVar, primInfo, argExpList: arg args, argTyList,
       *          resultTy,
       *          instTyList, instTagList: tag tags, instSizeList: size sizes,
       *          loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "prim" +d primInfo
       *   +1 tags:ifcons()("tag=(" !N0{ tags(tag)("," +1) ")" },)
       *   +1 tags:ifcons()("size=(" !N0{ sizes(size)("," +1) ")" },)
       *   +1 "(" !N0{ args(arg)("," +d) ")" }
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, primInfo, argExpList: arg args, argTyList,
       *          resultTy,
       *          instTyList, instTagList: tag tags, instSizeList: size sizes,
       *          loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "prim" +d primInfo
       *   +1 tags:ifcons()("tag=(" !N0{ tags(tag)("," +1) ")" },)
       *   +1 tags:ifcons()("size=(" !N0{ sizes(size)("," +1) ")" },)
       *   +1 "(" !N0{ args(arg)("," +d) ")" }
       *   ";"
       * ] }
       *)
      MCPRIMAPPLY of
      {
        resultVar : varInfo,
        primInfo : primInfo,
        argExpList : mcvalue list,
        argTyList : ty list,
        resultTy : ty,
        instTyList : ty list,
        instTagList : mcvalue list,
        instSizeList : mcvalue list,
        loc : loc
      }
    | (*%
       * @format({resultVar, exp, expTy, targetTy, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "bitcast"
       *   +1 exp
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar, exp, expTy, targetTy, loc})
       * L8{ 1[
       *   resultVar +d "="
       *   +1 "bitcast"
       *   +1 L2{ exp +1 ":" +d expTy }
       *   ";"
       * ] }
       *)
      MCBITCAST of
      {
        resultVar : varInfo,
        exp : mcvalue,
        expTy : ty,
        targetTy : ty,
        loc : loc
      }
    | (*%
       * @format({resultVar: resultVar resultVarOpt, resultTy, codeExp,
       *          closureEnvExp: clsEnv clsEnvOpt,
       *          argExpList: arg args, tail, handler: handler handlerOpt, loc})
       * L8{ 1[
       *   resultVarOpt:ifsome()(resultVarOpt(resultVar) +d "=" +1,)
       *   tail:iftrue()("tail" +1,)
       *   "call"
       *   +1 codeExp
       *   +1 clsEnvOpt:ifsome()(clsEnvOpt(clsEnv), "_")
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   handlerOpt:ifsome()(+1 "handle" +d handlerOpt(handler),)
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({resultVar: resultVar resultVarOpt, resultTy, codeExp,
       *          closureEnvExp: clsEnv clsEnvOpt,
       *          argExpList: arg args, tail, handler: handler handlerOpt, loc})
       * L8{ 1[
       *   resultVarOpt:ifsome()(resultVarOpt(resultVar) +d "=" +1,)
       *   tail:iftrue()("tail" +1,)
       *   "call"
       *   +1 codeExp
       *   +1 clsEnvOpt:ifsome()(clsEnvOpt(clsEnv), "_")
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   handlerOpt:ifsome()(+1 "handle" +d handlerOpt(handler),)
       *   ";"
       * ] }
       *)
      MCCALL of
      {
        resultVar : varInfo option,
        resultTy : ty,
        codeExp : mcvalue,
        closureEnvExp : mcvalue option,
        argExpList : mcvalue list,
        handler : HandlerLabel.id option,
        tail : bool,
        loc : loc
      }
    | (*%
       * @format({srcExp, srcTy, dstAddr, barrier, loc})
       * L8{ 1[
       *   "store"
       *   barrier:iftrue()(+1 "barrier",)
       *   +1 dstAddr
       *   +1 srcExp
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({srcExp, srcTy, dstAddr, barrier, loc})
       * L8{ 1[
       *   "store"
       *   barrier:iftrue()(+1 "barrier",)
       *   +1 dstAddr
       *   +1 srcExp
       *   ";"
       * ] }
       *)
      MCSTORE of
      {
        srcExp : mcvalue,
        srcTy : ty,
        dstAddr : address,
        barrier : bool,
        loc : loc
      }
    | (*%
       * @format({id, ty, valueExp, loc})
       * L8{ 1[
       *   "exportvar"
       *   +1 "@ext:" id
       *   +1 valueExp
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({id, ty, valueExp, loc})
       * L8{ 1[
       *   "exportvar"
       *   +1 "@ext:" id
       *   +1 valueExp
       *   ";"
       * ] }
       *)
      MCEXPORTVAR of
      {
        id : ExternSymbol.id,
        ty : ty,
        valueExp : mcvalue,
        loc : loc
      }
    | (*%
       * @format({value, loc})
       * L8{ 1[
       *  "keepalive"
       *  +1 value
       *  ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({value, loc})
       * L8{ 1[
       *  "keepalive"
       *  +1 value
       *  ";"
       * ] }
       *)
      MCKEEPALIVE of
      {
        value : mcvalue,
        loc : loc
      }

  (*%
   * @formatter(FunLocalLabel.id) FunLocalLabel.format_id
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(ifcons) ifcons
   * @formatter(iftrue) iftrue
   * @formatter(ifsome) ifsome
   *)
  (*% @prefix formatWithType_
   * @formatter(FunLocalLabel.id) FunLocalLabel.format_id
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(ifcons) ifcons
   * @formatter(iftrue) iftrue
   * @formatter(ifsome) ifsome
   *)
  datatype mcexp_last =
      (*%
       * @format({value, loc})
       * L8{ 1[
       *   "return"
       *   +1 value
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({value, loc})
       * L8{ 1[
       *   "return"
       *   +1 value
       *   ";"
       * ] }
       *)
      MCRETURN of
      {
        value : mcvalue,
        loc : loc
      }
    | (*%
       * @format({argExp, cleanup: cleanup cleanupOpt, loc})
       * L8{ 1[
       *   "raise"
       *   +1 argExp
       *   cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({argExp, cleanup: cleanup cleanupOpt, loc})
       * L8{ 1[
       *   "raise"
       *   +1 argExp
       *   cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   ";"
       * ] }
       *)
      MCRAISE of
      {
        argExp : mcvalue,
        cleanup : HandlerLabel.id option,
        loc : loc
      }
    | (*%
       * @format({nextExp, exnVar, id, handlerExp, cleanup: cleanup cleanupOpt,
       *          loc})
       * L8{ 1[ "available" +1 id ";" ] }
       * \n nextExp
       * L8{ 1[
       *   "handler" +1 id +1 "(" !N0{ exnVar ")" }
       *   cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   ":"
       * ] } \n
       * 1[ \n handlerExp ]
       *)
      (*% @prefix formatWithType_
       * @format({nextExp, exnVar, id, handlerExp, cleanup: cleanup cleanupOpt,
       *          loc})
       * L8{ 1[ "available" +1 id ";" ] }
       * \n nextExp
       * L8{ 1[
       *   "handler" +1 id +1 "(" !N0{ exnVar ")" }
       *   cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   ":"
       * ] }
       * 1[ \n handlerExp ]
       *)
      MCHANDLER of
      {
        nextExp : mcexp,
        id : HandlerLabel.id,
        exnVar : varInfo,
        handlerExp : mcexp,
        cleanup : HandlerLabel.id option,
        loc : loc
      }
    | (*%
       * @format({switchExp, expTy, branches: branch branches, default, loc})
       * L8{ 1[
       *   "switch"
       *   +1 switchExp
       *   +1 "[" !N0{ 1[
       *     1 branches(branch)("," +1)
       *     branches:ifcons()("," +1,) !N0{ "_" +d "=>" 1[ +1 default ] }
       *   ] 1 "]" }
       *   ";"
       * ] }
       * @format:branch(const * label)
       * !N0{ const +d "=>" 1[ +1 label ] }
       *)
      (*% @prefix formatWithType_
       * @format({switchExp, expTy, branches: branch branches, default, loc})
       * L8{ 1[
       *   "switch"
       *   +1 switchExp
       *   +1 "[" !N0{ 1[
       *     1 branches(branch)("," +1)
       *     branches:ifcons()("," +1,) !N0{ "_" +d "=>" 1[ +1 default ] }
       *   ] 1 "]" }
       *   ";"
       * ] }
       * @format:branch(const * label)
       * !N0{ const +d "=>" 1[ +1 label ] }
       *)
      MCSWITCH of
      {
        switchExp : mcvalue,
        expTy : ty,
        branches : (mcconst * FunLocalLabel.id) list,
        default : FunLocalLabel.id,
        loc : loc
      }
    | (*%
       * @format({id, recursive, argVarList : arg args, bodyExp, nextExp, loc})
       * L8{ 1[ "available" +1 id ";" ] }
       * \n nextExp
       * L8{ 1[
       *   "localcode"
       *   recursive:iftrue()(+1 "rec",)
       *   +1 id
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   ":"
       * ] }
       * 1[ \n bodyExp ]
       *)
      (*% @prefix formatWithType_
       * @format({id, recursive, argVarList : arg args, bodyExp, nextExp, loc})
       * L8{ 1[ "available" +1 id ";" ] }
       * \n nextExp
       * L8{ 1[
       *   "localcode"
       *   recursive:iftrue()(+1 "rec",)
       *   +1 id
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   ":"
       * ] }
       * 1[ \n bodyExp ]
       *)
      MCLOCALCODE of
      {
        id : FunLocalLabel.id,
        recursive : bool,
        argVarList : varInfo list,
        bodyExp : mcexp,
        nextExp : mcexp,
        loc : loc
      }
    | (*%
       * @format({id, argList: arg args, loc})
       * L8{ 1[
       *   "goto"
       *   +1 id
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   ";"
       * ] }
       *)
      (*% @prefix formatWithType_
       * @format({id, argList: arg args, loc})
       * L8{ 1[
       *   "goto"
       *   +1 id
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   ";"
       * ] }
       *)
      MCGOTO of
      {
        id : FunLocalLabel.id,
        argList : mcvalue list,
        loc : loc
      }
    | (*%
       * @format
       * "unreachable;"
       *)
      (*% @prefix formatWithType_
       * @format
       * "unreachable;"
       *)
      MCUNREACHABLE

  withtype mcexp =
      (*%
       * @format(mid mids * last)
       * mids(mid)(\n)
       * mids:ifcons()(\n,)
       * last
       *)
      (*% @prefix formatWithType_
       * @format(mid mids * last)
       * mids(mid)(\n)
       * mids:ifcons()(\n,)
       * last
       *)
      mcexp_mid list * mcexp_last

  (*%
   * @formatter(RuntimeCalc.topdata) RuntimeCalc.format_topdata
   *)
  (*% @prefix formatWithType_
   * @formatter(RuntimeCalc.topdata) RuntimeCalc.formatWithType_topdata
   *)
  datatype topdata = datatype RuntimeCalc.topdata

  fun formatSlotMap args smap =
      SMLFormat.BasicFormatters.format_list args (SlotID.Map.listItemsi smap)
  fun ifempty (x, y) smap =
      if SlotID.Map.isEmpty smap then x else y

  (* for pretty printing *)
  (*%
   * @formatter(SlotID.id) SlotID.format_id
   *)
  (*% @prefix formatWithType_
   * @formatter(SlotID.id) SlotID.format_id
   *)
  type 'a slotmap_field =
      (*%
       * @format(k * v)
       * k
       *)
      (*% @prefix formatWithType_
       * @format(k * v)
       * L2{ 1[ k +d  ":" +1 v ] }
       *)
      SlotID.id * 'a

  (*%
   * @formatter(FunEntryLabel.id) FunEntryLabel.format_id
   * @formatter(CallbackEntryLabel.id) CallbackEntryLabel.format_id
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(SlotID.Map.map) formatSlotMap
   * @formatter(RuntimeTypes.ty) RuntimeTypes.format_ty
   * @formatter(ifempty) ifempty
   * @formatter(ifsome) ifsome
   * @formatter(iftrue) iftrue
   *)
  (*% @prefix formatWithType_
   * @formatter(FunEntryLabel.id) FunEntryLabel.format_id
   * @formatter(CallbackEntryLabel.id) CallbackEntryLabel.format_id
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(SlotID.Map.map) formatSlotMap
   * @formatter(RuntimeTypes.ty) RuntimeTypes.format_ty
   * @formatter(ifempty) ifempty
   * @formatter(ifsome) ifsome
   * @formatter(iftrue) iftrue
   *)
  datatype topdec =
      (*%
       * @format({id, tyvarKindEnv, argVarList: arg args,
       *          closureEnvVar: env envOpt, frameSlots: slot slots, bodyExp,
       *          retTy, gcCheck, loc})
       * L8{ 2[
       *   "fun"
       *   +1 id
       *   +1 envOpt:ifsome()(envOpt(env),"_")
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   gcCheck:iftrue()(+1 "gc",)
       *   +d "="
       * ] }
       * 1[ \n
       *   slots:ifempty()(,
       *     L8{ 1[
       *       "frameslots"
       *       +1 slots(slot:slotmap_field(slot))("," +1)
       *       ";"
       *     ] } \n
       *   )
       *   bodyExp
       * ]
       *)
      (*% @prefix formatWithType_
       * @format({id, tyvarKindEnv, argVarList: arg args,
       *          closureEnvVar: env envOpt, frameSlots: slot slots, bodyExp,
       *          retTy, gcCheck, loc})
       * L8{ 2[
       *   "fun"
       *   +1 id
       *   +1 envOpt:ifsome()(envOpt(env),"_")
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   gcCheck:iftrue()(+1 "gc",)
       *   +d "="
       * ] }
       * 1[ \n
       *   slots:ifempty()(,
       *     L8{ 1[
       *       "frameslots"
       *       +1 slots(slot:slotmap_field(slot))("," +1)
       *       ";"
       *     ] } \n
       *   )
       *   bodyExp
       * ]
       *)
      MTFUNCTION of
      {
        id : FunEntryLabel.id,
        tyvarKindEnv : Types.btvEnv,
        argVarList : varInfo list,
        closureEnvVar : varInfo option,
        frameSlots : RuntimeTypes.ty SlotID.Map.map,
        bodyExp : mcexp,
        retTy : ty,
        gcCheck : bool,
        loc : loc
      }
    | (*%
       * @format({id, tyvarKindEnv, argVarList: arg args,
       *          closureEnvVar: env envOpt, frameSlots: slot slots, bodyExp,
       *          attributes, retTy, cleanupHandler: cleanup cleanupOpt, loc})
       * L8{ 2[
       *   "callback"
       *   +1 id
       *   +1 envOpt:ifsome()(envOpt(env),"_")
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   +1 cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   +d "="
       * ] }
       * 1[ \n
       *   slots:ifempty()(,
       *     L8{ 1[
       *       "frameslots"
       *       +1 slots(slot:slotmap_field(slot))("," +1)
       *       ";"
       *     ] } \n
       *   )
       *   bodyExp
       * ]
       *)
      (*% @prefix formatWithType_
       * @format({id, tyvarKindEnv, argVarList: arg args,
       *          closureEnvVar: env envOpt, frameSlots: slot slots, bodyExp,
       *          attributes, retTy, cleanupHandler: cleanup cleanupOpt, loc})
       * L8{ 2[
       *   "callback"
       *   +1 id
       *   +1 envOpt:ifsome()(envOpt(env),"_")
       *   +1 "(" !N0{ args(arg)("," +1) ")" }
       *   +1 cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   +d "="
       * ] }
       * 1[ \n
       *   slots:ifempty()(,
       *     L8{ 1[
       *       "frameslots"
       *       +1 slots(slot:slotmap_field(slot))("," +1)
       *       ";"
       *     ] } \n
       *   )
       *   bodyExp
       * ]
       *)
      MTCALLBACKFUNCTION of
      {
        id : CallbackEntryLabel.id,
        tyvarKindEnv : Types.btvEnv,
        argVarList : varInfo list,
        closureEnvVar : varInfo option,
        frameSlots : RuntimeTypes.ty SlotID.Map.map,
        bodyExp : mcexp,
        attributes : FFIAttributes.attributes,
        retTy : ty option,
        cleanupHandler : HandlerLabel.id option,
        loc : loc
      }

  (*%
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(SlotID.id) SlotID.format_id
   * @formatter(SlotID.Map.map) formatSlotMap
   * @formatter(RuntimeTypes.ty) RuntimeTypes.format_ty
   * @formatter(ifempty) ifempty
   * @formatter(ifsome) ifsome
   *)
  (*% @prefix formatWithType_
   * @formatter(HandlerLabel.id) HandlerLabel.format_id
   * @formatter(SlotID.Map.map) formatSlotMap
   * @formatter(RuntimeTypes.ty) RuntimeTypes.format_ty
   * @formatter(ifempty) ifempty
   * @formatter(ifsome) ifsome
   *)
  type toplevel =
      (*%
       * @format({frameSlots: slot slots, bodyExp,
       *          cleanupHandler: cleanup cleanupOpt})
       * L8{ 2[
       *   "toplevel"
       *   cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   +d "="
       * ] }
       * 1[ \n
       *   slots:ifempty()(,
       *     L8{ 1[
       *       "frameslots"
       *       +1 slots(slot:slotmap_field(slot))("," +1)
       *       ";"
       *     ] } \n
       *   )
       *   bodyExp
       * ]
       *)
      (*% @prefix formatWithType_
       * @format({frameSlots: slot slots, bodyExp,
       *          cleanupHandler: cleanup cleanupOpt})
       * L8{ 2[
       *   "toplevel"
       *   cleanupOpt:ifsome()(+1 "cleanup" +d cleanupOpt(cleanup),)
       *   +d "="
       * ] }
       * 1[ \n
       *   slots:ifempty()(,
       *     L8{ 1[
       *       "frameslots"
       *       +1 slots(slot:slotmap_field(slot))("," +1)
       *       ";"
       *     ] } \n
       *   )
       *   bodyExp
       * ]
       *)
      {
        frameSlots : RuntimeTypes.ty SlotID.Map.map,
        bodyExp : mcexp,
        cleanupHandler : HandlerLabel.id option
      }

  (*%
   * @formatter(ifcons) ifcons
   *)
  (*% @prefix formatWithType_
   * @formatter(ifcons) ifcons
   *)
  type program =
      (*%
       * @format({topdata: datum data, topdecs: dec decs, toplevel})
       * data(datum)(\n) data:ifcons()("\n",)
       * decs(dec)(\n) decs:ifcons()("\n",)
       * toplevel
       *)
      (*% @prefix formatWithType_
       * @format({topdata: datum data, topdecs: dec decs, toplevel})
       * data(datum)(\n) data:ifcons()("\n",)
       * decs(dec)(\n) decs:ifcons()("\n",)
       * toplevel
       *)
      {
        topdata : topdata list,
        topdecs : topdec list,
        toplevel : toplevel
      }

end
